package com.ingenico.insider.services.impl;

import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.logging.Level;
import java.util.logging.Logger;

import sun.reflect.generics.reflectiveObjects.NotImplementedException;

import com.ingenico.insider.model.PCurveListModel;
import com.ingenico.insider.nodes.PSampleChannel;
import com.ingenico.piccolo.event.CameraModificationTracker;
import com.ingenico.tools.data.chopping.VersatileChopper;
import com.ingenico.tools.data.sampling.PathSampler;
import com.ingenico.tools.nio.channel.SampleFileChannel;
import com.ingenico.tools.nio.channel.SampleLength;
import com.ingenico.tools.nio.channel.SampleSign;

import edu.umd.cs.piccolo.PLayer;

public final class CurvesSupplier {
	private static final Logger _logger = Logger.getLogger(CurvesSupplier.class.getCanonicalName());

	public static final String NOTFOUND_MESSAGE_KEY = "notfound";
	public static final String IO_MESSAGE_KEY = "io";

	/**
	 * singleton instance
	 */
	private static CurvesSupplier instance;

	private final PLayer curvesLayer;

	/**
	 * The data model that maintains the curves list
	 */
	private final PCurveListModel curvesModel;

	/**
	 * The camera observer that will keep track of camera changes 
	 */
	private final CameraModificationTracker cameraObserver;

	/**
	 * private constructor of final class to prevent external instantiation
	 */
	private CurvesSupplier() {
		final CanvasSupplier canvasSupplier = CanvasSupplier.getInstance();
		curvesModel = new PCurveListModel();
		cameraObserver = new CameraModificationTracker(canvasSupplier.getJComponent());
		curvesLayer = canvasSupplier.getCanvas().getLayer();

		// Y axis is inverted in screen coordinates... let's scale it to a math reference
		curvesLayer.setScale(1, -1);
		canvasSupplier.getCanvas().getCamera().translateView(0, 255);

	}

	/**
	 * retrieve the singleton instance
	 *
	 * @return the CurvesSupplier singleton
	 */
	public static CurvesSupplier getInstance() {
		if (instance == null) {
			_logger.info(CurvesSupplier.class.getName() + " instance does not exist... Creating instance.");
			instance = new CurvesSupplier();
		}
		return instance;
	}

	public void loadRawFile (final File file, final SampleLength sampleLength, final SampleSign sign, final ByteOrder order, final int sampleRate)
	throws IOException {
		loadRawFile (
			file,
// FIXME : This is broken... I don't know precisely why, but we need to allocate a new instance every time...
			SubSamplingStrategySupplier.getInstance().buildStrategy(null),
//			new AverageFloatPathSampler(),
			sampleLength,
			sign,
			order,
			sampleRate,
			CyclicColorsSupplier.getInstance().getNext()
		);
	}

	public void loadRawFile(final File file, final PathSampler pathSampler, final SampleLength sampleLength, final SampleSign sign, final ByteOrder order, final int sampleRate, final Color color)
	throws IOException {
		final SampleFileChannel sampleChannel = SampleFileChannel.createSampleFileChannel(file, sampleLength, sign, order, sampleRate);
		final PSampleChannel channelNode = new PSampleChannel();

		channelNode.setDataChannel(sampleChannel);

		curvesLayer.addChild(channelNode);
		VersatileChopper choppingStrategy = new VersatileChopper();
// FIXME: versatile chopping strategy seems rather bugged with the following settings...
// I believe it comes from the PThreadedChoppingNode addChild Method that only add 5 children at a time and constantly cleans children list...
		choppingStrategy.setMaxChuncks(6);
//		choppingStrategy.setMaxSamples(500000);
		channelNode.addChild(pathSampler, choppingStrategy);
//		channelNode.addChild(PathSamplingStrategySupplier.getInstance().getAvailableStrategies()[0], new VersatileChopper());
//		channelNode.addChild(PathSamplingStrategySupplier.getInstance().getAvailableStrategies()[2], new VersatileChopper());
//		channelNode.addChild(PathSamplingStrategySupplier.getInstance().getAvailableStrategies()[3], new VersatileChopper());

		channelNode.setStrokePaint(color);
		channelNode.addAttribute(PSampleChannel.FILE_KEY, file);

		// Add the group of re-sampled curves to the model and to the canvas
		curvesModel.addElement(channelNode);

		curvesLayer.invalidateFullBounds();
	}

	public void loadAuFile (final File file)
	throws IOException {
		loadAuFile (
			file,
			SubSamplingStrategySupplier.getInstance().buildStrategy(null),
//			SubSamplingStrategySupplier.getInstance().getDefaultStrategy(),
			CyclicColorsSupplier.getInstance().getNext()
		);
	}

	public void loadAuFile (final File file, final PathSampler pathSampler, final Color color)
	throws IOException {
//TODO : implement
		throw new NotImplementedException();
	}

	public void save (final File file)
	throws IOException {
//TODO: implement
		throw new NotImplementedException();
	}

	public void close(PSampleChannel channelNode) {
		_logger.finer("Removing Downsampling Nodes from \"" + channelNode + "\"...");
		channelNode.removeAllChildren();

		_logger.finer("Removing " + channelNode + " from Canvas...");
		curvesLayer.removeChild(channelNode);
		
		_logger.finer("Removing " + channelNode + " from DataModel...");
		curvesModel.removeElement(channelNode);

		try {
			_logger.finer("Closing DataChannel from \"" + channelNode + "\"...");
			channelNode.getDataChannel().close();
		} catch (IOException ioe) {
			_logger.log(Level.SEVERE, ioe.getMessage(), ioe);
			MessageBoxSupplier.getInstance().showErrorDialog(DockableViewsSupplier.IO_MESSAGE_KEY, ioe);
		}
	}

	public PCurveListModel getModel() {
		return curvesModel;
	}

	public CameraModificationTracker getCameraObserver() {
		return cameraObserver;
	}
}
